Skip to content

Scope the likes collection group to the requesting user - #184

Merged
renrenmimi merged 1 commit into
mainfrom
fix/likes-collection-group-scope
Sep 4, 2026
Merged

Scope the likes collection group to the requesting user#184
renrenmimi merged 1 commit into
mainfrom
fix/likes-collection-group-scope

Conversation

@renrenmimi

Copy link
Copy Markdown
Owner

collectionGroup('likes').where('userId','==',victim) returned anyone's complete like history — every post they have ever liked, in one query, to an unauthenticated caller. The composite index exists, so it was servable today.

Why per-post likes stay public

Two different rules, two different questions:

rule question verdict
posts/{postId}/likes "who liked this post?" stays read: if true
{path=**}/likes (CG) "every like matching a filter, across all posts" now scoped to the requester

"These 40 accounts liked this photo" and "here is everything this person has ever liked" are different facts about someone. No UI has ever exposed the second one.

Blast radius: none

The only client that touches the collection group is useBatchLikeStatus, which already filters by the signed-in user's own uid. There's a test pinning its exact query shape — userId == me AND postId in [...] — so a future tightening can't silently strip like state off every feed card.

Tests

tests/rules/ban-and-engagement.test.ts, 25 → 30.

Three fail on the old rule, pass on this one:

× refuses an unauthenticated cross-post query for one person's likes
× refuses a signed-in user querying someone else's like history
× refuses an unfiltered collection-group scan

Two pass on both, as guardrails: an unauthenticated read of a single post's likes, and the batched own-likes query the feed depends on.

Local run: rules 62/62.

Checkins are NOT in this PR — and the reason matters

The audit reported checkins alongside likes, and the instruction was to constrain both. I did not, because constraining checkins to the requesting user breaks a shipped feature, and doing only the userId half would not close the exposure anyway. Details in the PR discussion — this needs a decision, not a guess.

Deploy

firebase deploy --only firestore:rules

🤖 Generated with Claude Code

`collectionGroup('likes').where('userId','==',victim)` returned anyone's
complete like history — every post they have ever liked, in one query, to an
unauthenticated caller. The composite index exists, so it was servable today.

Per-post likes stay world-readable and that is deliberate: a post's like count
and its likers are public, and that is the only shape any UI needs. The
collection group answers a different question — "every like matching a filter,
across all posts" — and no UI has ever exposed that aggregate. "These 40
accounts liked this photo" and "here is everything this person has ever liked"
are different facts about someone.

The only client that uses the collection group is useBatchLikeStatus, which
already filters by the signed-in user's own uid, so it is unaffected. There is
a test pinning exactly its query shape (userId == me AND postId in [...]) so
this cannot be tightened further by accident and silently strip like state off
every feed card.

Three new tests fail on the old rule and pass on this one: the unauthenticated
cross-post query, a signed-in user reading someone else's history, and an
unfiltered scan. Two more pass on both, as guardrails: an unauthenticated read
of a single post's likes, and the batched own-likes query.

Checkins were reported alongside this and are NOT changed here — the same
tightening would break the public per-pet check-in history on PetProfile, and
constraining only the userId path would not close the exposure anyway. Raised
separately rather than half-fixed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 4, 2026 00:46
@vercel

vercel Bot commented Sep 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
pet-note Ready Ready Preview Sep 4, 2026 12:46am UTC

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@renrenmimi
renrenmimi merged commit cb45bfd into main Sep 4, 2026
6 checks passed
@renrenmimi
renrenmimi deleted the fix/likes-collection-group-scope branch September 4, 2026 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants